package com.jiyinit.web.core.activiti.service;

import com.jiyinit.core.framework.base.util.Check;
import org.activiti.engine.*;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.task.Task;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipInputStream;

/**
 * <p></p>
 * <p>
 * <PRE>
 * <BR>	修改记录
 * <BR>-----------------------------------------------
 * <BR>	修改日期			修改人			修改内容
 * </PRE>
 *
 * @author lishaochuan
 * @version 1.0
 * @date 2017/5/25 9:44
 * @since 1.0
 */
@Service("activitiServiceImpl")
public class ActivitiServiceImpl {

    /**
     * 日志对象
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ActivitiServiceImpl.class);


    @Autowired
    private TaskService taskService;

    @Autowired
    ProcessEngine processEngine;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;




    /**
     * 部署流程
     *
     * @author lishaochuan
     * @create 2017/7/21 15:57
     * @param
     * @return
     */
    public ProcessDefinition deployProcess(MultipartFile flowFile) throws IOException {
        String fileName = flowFile.getOriginalFilename();
        InputStream fileInputStream = flowFile.getInputStream();
        Deployment deployment;
        String extension = FilenameUtils.getExtension(fileName);
        if (extension.equals("zip") || extension.equals("bar")) {
            ZipInputStream zip = new ZipInputStream(fileInputStream);
            deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
        } else {
            deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
        }
        String deploymentId = deployment.getId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        return processDefinition;
    }


    /**
     * 获取流程定义所有环节信息
     * @author lishaochuan
     * @create 2017/7/21 19:14
     * @param
     * @return
     */
    public List<Map<String, String>> getAllJobsByKey(String processKey){
        List<Map<String, String>> jobList = new ArrayList();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processKey).latestVersion().singleResult();
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processDefinition.getId());
        List<ActivityImpl> activitiList = def.getActivities();  //rs是指RepositoryService的实例
        for(ActivityImpl activityImpl : activitiList){
            String type = activityImpl.getProperty("type").toString();
            if (!(type.endsWith("Gateway") || type.equalsIgnoreCase("endEvent") || type.equalsIgnoreCase("startEvent"))) {
                Map<String, String> map = new HashMap<>();
                map.put("taskKey", activityImpl.getId());
                map.put("taskName", activityImpl.getProperty("name")+"");
                jobList.add(map);
            }
        }
        return jobList;
    }

    /**
     * 终止流程
     *
     * @param
     * @return
     * @author lishaochuan
     * @create 2017/5/27 17:29
     */
    public void stopProcess(String processInstId) {
        runtimeService.deleteProcessInstance(processInstId, "人工终止");
    }


    /**
     * 获取下一节点类型，如果是结束网关，就再获取下一节点
     *
     * @param
     * @return exclusive分支，inclusive包含， parallel并发， task任务节点或结束节点，end结束
     * @author lishaochuan
     * @create 2017/5/25 10:42
     */
    public String getNextType(String procInstanceId, String taskKey) {
        String nextType = "";
        // 流程标示
        String processDefinitionId = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstanceId).singleResult().getProcessDefinitionId();
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processDefinitionId);
        // 获得当前任务的所有节点
        for (ActivityImpl activityImpl : def.getActivities()) {
            String key = activityImpl.getId();
            if (taskKey.equals(key)) {
                List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
                if (outTransitions.size() > 0) {
                    PvmTransition tr = outTransitions.get(0);
                    PvmActivity ac = tr.getDestination(); //获取线路的下一节点
                    nextType = ac.getProperty("type").toString();

                    // 结束网关
                    /*String nextId = ac.getId().toLowerCase();
                    if(nextType.endsWith("Gateway") && nextId.endsWith("_end")){
                        List<PvmTransition> outTransitionsTemp = ac.getOutgoingTransitions();
                        if (outTransitionsTemp.size() > 0){
                            PvmTransition tr1 = outTransitionsTemp.get(0);
                            PvmActivity ac1 = tr1.getDestination(); //获取线路的下一节点
                            nextType = ac1.getProperty("type").toString();
                        }
                    }*/

                    // 如果是包含网关、并发网关，判断是否是结束网关
                    if (nextType.equalsIgnoreCase("inclusiveGateway") || nextType.equalsIgnoreCase("parallelGateway")) {
                        List<PvmTransition> incomingTransitions = ac.getIncomingTransitions();
                        // 如果进入分支大于2，则认为是结束网关
                        if (incomingTransitions.size() >= 2) {
                            //获取线路的下一节点
                            List<PvmTransition> outTransitionsTemp = ac.getOutgoingTransitions();
                            if (outTransitionsTemp.size() > 0) {
                                PvmTransition tr1 = outTransitionsTemp.get(0);
                                PvmActivity ac1 = tr1.getDestination();
                                nextType = ac1.getProperty("type").toString();
                            }
                        }
                    }

					/*
                    ("userTask", "用户任务")
			        ("serviceTask", "系统任务")
			        ("startEvent", "开始节点")
			        ("endEvent", "结束节点")
			        ("exclusiveGateway", "分支判断节点")
			        ("inclusiveGateway", "包含处理节点")
			        ("parallelGateway", "并发处理节点")
			        ("callActivity", "子流程")
			        */
                    /*if (nextType.equalsIgnoreCase("exclusiveGateway")) {
                        resultType = "exclusive"; //分支
                    } else if (nextType.equalsIgnoreCase("inclusiveGateway")) {
                        resultType = "inclusive"; //包含
                    } else if (nextType.equalsIgnoreCase("parallelGateway")) {
                        resultType = "parallel"; //并发
                    } else if(nextType.equalsIgnoreCase("endEvent")){
                        resultType = "end"; //结束
                    } else if(nextType.equalsIgnoreCase("userTask")) {
                        resultType = "task"; //任务节点
                    } else {
                        resultType = nextType;
                    }*/
                    return nextType;
                }
            }
        }
        return nextType;
    }


    /**
     * 获取当前运行的下一节点集合
     *
     * @param
     * @return
     * @author lishaochuan
     * @create 2017/5/9 11:46
     */
    public List<Task> getRunningTasks(String processInstId, String taskKey) {
        // 查询当前运行的任务节点
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstId).list();
        List<Task> nextTasks = new ArrayList<>();
        // 取流程图上下一节点key集合
        Map<String, String> nextTaskMaps = this.getNextTaskMaps(processInstId, taskKey);
        Set<String> nextTaskKeys = nextTaskMaps.keySet();

        // 取交集
        for (Task task : taskList) {
            if (nextTaskKeys.contains(task.getTaskDefinitionKey())) {
                nextTasks.add(task);
            }
        }
        return nextTasks;
    }


    /**
     * 获取流程图上下一步节点集合
     *
     * @param
     * @return
     * @author lishaochuan
     * @create 2017/5/9 9:37
     */
    public Map<String, String> getNextTaskMaps(String procInstanceId, String taskKey) {
        Map<String, String> map = new HashMap<>();
        // 流程标示
        String processDefinitionId = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstanceId).singleResult().getProcessDefinitionId();
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processDefinitionId);
        // 获得当前任务的所有节点
        for (ActivityImpl activityImpl : def.getActivities()) {
            String key = activityImpl.getId();
            if (taskKey.equals(key)) {
                List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
                jumpGateway(map, outTransitions);
            }
        }
        return map;
    }


    /**
     * 递归跳过网关节点，直到取到非网关节点为止
     *
     * @param
     * @return
     * @author lishaochuan
     * @create 2017/5/9 9:37
     */
    private void jumpGateway(Map<String, String> map, List<PvmTransition> outTransitions) {
        for (PvmTransition tr : outTransitions) {
            //获取线路的下一节点
            PvmActivity ac = tr.getDestination();
            String type = ac.getProperty("type").toString();
            if (type.endsWith("Gateway")) {
                List<PvmTransition> outTransitionsTemp = ac.getOutgoingTransitions();
                jumpGateway(map, outTransitionsTemp);
            } else if (type.endsWith("Task")) {
                //Map map = new HashMap();
                //map.put("key", ac.getId());
                //map.put("name", ac.getProperty("name"));
                //list.add(map);
                map.put(ac.getId(), ac.getProperty("name")+"");
            }
        }
    }


    /**
     * 流程转向操作
     *
     * @param taskId     当前任务ID
     * @param activityId 目标节点任务ID
     * @param variables  流程变量
     * @throws Exception
     */
    public void turnTransition(String taskId, String activityId, Map<String, Object> variables) {
        // 当前节点
        ActivityImpl currActivity = findActivitiImpl(taskId, null);
        // 清空当前流向
        List<PvmTransition> oriPvmTransitionList = clearTransition(currActivity);
        // 创建新流向
        TransitionImpl newTransition = currActivity.createOutgoingTransition();
        // 目标节点
        ActivityImpl pointActivity = findActivitiImpl(taskId, activityId);
        // 设置新流向的目标节点
        newTransition.setDestination(pointActivity);
        // 执行转向任务
        taskService.complete(taskId, variables);
        // 删除目标节点新流入
        pointActivity.getIncomingTransitions().remove(newTransition);
        // 还原以前流向
        restoreTransition(currActivity, oriPvmTransitionList);
    }


    /**
     * 根据任务ID和节点ID获取活动节点
     *
     * @param taskId     任务ID
     * @param activityId 活动节点ID
     *                   如果为null或""，则默认查询当前活动节点
     *                   如果为"end"，则查询结束节点
     * @return
     * @throws Exception
     */
    private ActivityImpl findActivitiImpl(String taskId, String activityId) {
        // 取得流程定义
        ProcessDefinitionEntity processDefinition = findProcessDefinitionEntityByTaskId(taskId);
        // 获取当前活动节点ID
        if (Check.isNullStr(activityId)) {
            activityId = findTaskById(taskId).getTaskDefinitionKey();
        }
        // 根据流程定义，获取该流程实例的结束节点
        if (activityId.toUpperCase().equals("END")) {
            for (ActivityImpl activityImpl : processDefinition.getActivities()) {
                List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
                if (pvmTransitionList.isEmpty()) {
                    return activityImpl;
                }
            }
        }
        // 根据节点ID，获取对应的活动节点
        ActivityImpl activityImpl = ((ProcessDefinitionImpl) processDefinition).findActivity(activityId);
        return activityImpl;
    }

    /**
     * 清空指定活动节点流向
     *
     * @param activityImpl 活动节点
     * @return 节点流向集合
     */
    private List<PvmTransition> clearTransition(ActivityImpl activityImpl) {
        // 存储当前节点所有流向临时变量
        List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
        // 获取当前节点所有流向，存储到临时变量，然后清空
        List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
        for (PvmTransition pvmTransition : pvmTransitionList) {
            oriPvmTransitionList.add(pvmTransition);
        }
        pvmTransitionList.clear();
        return oriPvmTransitionList;
    }

    /**
     * 还原指定活动节点流向
     *
     * @param activityImpl         活动节点
     * @param oriPvmTransitionList 原有节点流向集合
     */
    private void restoreTransition(ActivityImpl activityImpl, List<PvmTransition> oriPvmTransitionList) {
        // 清空现有流向
        List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
        pvmTransitionList.clear();
        // 还原以前流向
        for (PvmTransition pvmTransition : oriPvmTransitionList) {
            pvmTransitionList.add(pvmTransition);
        }
    }

    /**
     * 根据任务ID获取流程定义
     *
     * @param taskId 任务ID
     * @return
     * @throws Exception
     */
    private ProcessDefinitionEntity findProcessDefinitionEntityByTaskId(String taskId) {
        // 取得流程定义
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(findTaskById(taskId).getProcessDefinitionId());
        return processDefinition;
    }

    /**
     * 根据任务ID获得任务实例
     *
     * @param taskId 任务ID
     * @return
     * @throws Exception
     */
    private TaskEntity findTaskById(String taskId) {
        TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        return task;
    }
    

    
}
